"""
persistence.py
Created by Gleicon Moraes on 2009-03-02.

Set of classes and methods for persistence support (thomas.py, reverend)

"""

#import sqlite3 as sqlite
from bayesdata import BayesData, PoolStorage
from google.appengine.ext import db
import logging

class Pickler:
	def __init__(self, fname=None):
		if fname == None:
			self.fname = 'sentiment.dat'
		else:
			self.fname = fname

	def save(self, fname, pools):
		from cPickle import dump
		fp = open(self.fname, 'wb')
		dump(pools, fp)
		fp.close()

	def load(self, fname):
		print "loading from "+fname
		from cPickle import load
		fp = open(self.fname, 'rb')
		pools = load(fp)
		fp.close()

		for p in pools:

			b = pools[p].values()
                        tc = reduce(lambda x,y: x+y, b)
			print "%s  len: %d totalcount: %d" % (p, len(b), tc)
		return pools

	def commit(self, fname, pools):
		# for pickled db, commit is an alias to save
		self.save(fname, pools)

	def update(self, pool, token, ct):
		# print "update - not implemented yet"
		pass

	def delete(self, pool, token):
		print "delete - not implemented yet"
		pass
	
	def create_table(self, poolName):
		print "create - not implemented yet"
		pass

	def delete_table(self, poolName):
		print "delete - not implemented yet"
		pass

	def rename_table(self, poolName, newName):
		print "rename - not implemented yet"
		pass

	def merge_tables(self, sourcePool, destPool):
		print "merge - not implemented yet"
		pass

	def pool_names(self):
		print "pool_names - not implemented yet"
		pass

class SQLitePersistence(Pickler):
	def __init__(self, fname=None):
		if fname == None:
			self.fname = 'bayesdict.sqlite'
		else:
			self.fname = fname

		self.con=sqlite.connect(self.fname)
		self.con.text_factory = str

	def save(self, fname, pools):
		# no need to do a full export (each classification lends new updates)
		pass

	def load(self, fname):
		# import full database
		pools = {}
		pn = self.pool_names()
		if pn == None: 
			return None
		for p in pn:
			print "Reading pool %s" % (p)
			a = "select token, count from %s" % (p)
			r = self.con.execute(a).fetchall()
			if len(r) == 0:
				# empty table
				return None
			poolName = p[0]
			lero = dict(r)
			pools[poolName]=BayesData(poolName)
			pools[poolName].update(lero)
                        # select count()
                        b = lero.values()
			if b == None or b == 0:
				tc = 0
			else:
	                        tc = reduce(lambda x,y: x+y, b)
			pools[poolName].tokenCount = tc
			pools[poolName].trainCount = len(lero)
			print "%s  len: %d totalcount: %d" % (poolName, len(lero), tc)
                return pools

	def commit(self, fname, pools):
		self.con.commit()
	
	def update(self, pool, token, ct):
		try:
			a = self.con.total_changes
			uga = "select count from %s where token='%s'" % (pool.name, token)
			fa = self.con.execute(uga).fetchall()
			
			if len(fa) == 0:
				count = ct
			else:
				count = int(fa[0][0]) + ct
			self.con.execute("update "+pool.name+" set count=%d where token='%s'" % (count, token)) # era pool[token]
			if self.con.total_changes == a:
				self.con.execute("insert into "+pool.name+" values('%s', %d)" % (token, count))
		except sqlite.OperationalError, e:
        		print "An operational error occurred:", e.args[0] +". Trying to create table."
			self.create_table(pool)
			self.update(pool, token, ct)
		except sqlite.Error, e:
        		print "An error occurred:", e.args[0]

	def delete(self, pool, token):
		try:
			q = "delete from "+pool.name+" where key = '%s'" % (token)
			self.con.execute(q)
		except sqlite.Error, e:
        		print "An error occurred:", e.args[0] +". Deleting token: "+token
		
	def create_table(self, pool):
		try:
			self.con.execute("create table if not exists %s (token VARCHAR(256) PRIMARY KEY NOT NULL, count)" % (pool.name)) 
			self.pool_names()
		except sqlite.Error, e:
			print "An error occurred:", e.args[0]


	def delete_table(self, pool):
		try:
			print "Dropping table: %s " % (pool.name)
			q = "drop table %s" % (pool.name)
			self.con.execute(q)
		except sqlite.Error, e:
			print "An error occurred:", e.args[0]
			print pool


	def rename_table(self, poolName, newName):
		print "alter table.... "+poolName+" to "+newName

	def merge_tables(self, sourcePool, destPool):
		print "merge tables ...."+sourcePool+" to "+destPool

	def pool_names(self):
		try:
			q = "select name from sqlite_master where type='table'"
			r = self.con.execute(q)
			if r == None:
				print "No tables/pool in database"
				return None
			else:
				a = r.fetchall()
				if len(a) == 0:
					return None
				else:
					return a

		except sqlite.Error, e:
			print "An error occurred:", e.args[0]


class AppEngineStore(Pickler):
	def __init__(self, fname=None):
		if fname == None:
			self.fname = 'sentiment.dat'
		else:
			self.fname = fname

	def save(self, fname, pools):
		pass

	def load(self, fname=None):
		pools = {}
		pn = ("__Corpus__", "negative", "positive") # pegar com self.pool_names()
		if pn == None: 
			return None
		for p in pn:
			logging.info("Reading pool %s" % (p))
			a = "SELECT * from PoolStorage where pool = '%s'" % (p)
			ps = db.GqlQuery(a)
			
			if ps.count() == 0:
				# empty pool
				pools[p]=BayesData(p)
				pools[p].tokenCount = 0
				continue
			
			 # use iterable lero = dict(r) and create lero as dict
			#all = ps.fetch() # max 1000 entities, appengine limitation
			lero = {}
			for entity in ps:
				lero[entity.token]=entity.ct

			pools[p]=BayesData(p)
			pools[p].update(lero)
                        # select count()
                        b = lero.values()
			if b == None or b == 0:
				tc = 0
			else:
	                        tc = reduce(lambda x,y: x+y, b)
			pools[p].tokenCount = tc
			pools[p].trainCount = len(lero)
			logging.info("%s  len: %d totalcount: %d" % (p, len(lero), tc))
                return pools

	def commit(self, fname, pools):
		pass
	
	def update(self, pool, token, ct):
		q = "SELECT * from PoolStorage where pool = '%s' and token = '%s' LIMIT 1" % (pool.name, token)
		uga = db.GqlQuery(q)
		count = t = 0
		if uga.count() == 0:
			count = ct
		else:
			try:
				for a in uga:
					count+=a.ct
			except IndexError:
				count=ct
			
			count = t + ct
		
		ps = PoolStorage(pool=pool.name, token=token, ct=count)
		ps.put()

